public class Case_Trigger{

    //
    // Statics
    // 
    public static CaseTriggerHelpers.Log4Case l4c = new CaseTriggerHelpers.Log4Case();
    
    // Flag to block trigger invokations
    public static boolean firstTime = True;
    
    //
    // Trigger entry points & tests
    //
    public static testMethod void testDoBeforeInsertBatch() {
        Case c1 = new Case(Subject='E2 TEST CASE c1');
        Case c2 = new Case(Subject='E2 TEST CASE c2');
        Case[] carr1 = new Case[]{ c1, c2 };
        DoBeforeInsertBatch(carr1);
    }
    
    public static testMethod void testDoBeforeInsertBatch_SingleRecord() {
        Case c1 = new Case(Subject='E2 TEST CASE c1');
        Case[] carr1 = new Case[]{ c1 };
        DoBeforeInsertBatch(carr1);
    }

    public static void DoBeforeInsertBatch(Case[] newCases) {
        //l4c = new CaseTriggerHelpers.Log4Case();
        integer cnt = newCases.size();
        l4c.add('DoBeforeInsertBatch:  ' + cnt + ' cases');
        integer i=0;
        for(i=0;i<cnt;i++) {
            string nowStr = string.ValueOf(system.now());
            Case c = newCases[i];
            l4c.add('Inserting a case').incIndent();
            try {
                DoBeforeInsert(c);
                c.Last_Recalc__c = nowStr + ' (OK)';
            } catch (CaseTriggerHelpers.E2CalcsBadInputException e2ex) {
                system.debug( 'E2 Exception caught on Case insert:  ' + e2ex.getErrVal() );
                c.Last_Recalc__c = nowStr + e2ex.getErrVal();
            } catch (Exception e) { 
                system.debug( 'Unhandled exception on insert '+i+' of '+cnt+':  ' + e.getMessage() );
            } finally {
                system.debug('>>> L4C:  '+l4c.decIndent().add('Done with case').getAllLines());
                l4c.writeOut(c).clear();
            }
        }
        system.debug('>>> L4C:  '+l4c.decIndent().add('Done with case').getAllLines());
        l4c.clear();
    }
    
    public static testMethod void testDoBeforeUpdateBatch() {
        Case c1 = new Case(Subject='aaa E2 TEST CASE c1'); 
        Case c2 = new Case(Subject='aaa E2 TEST CASE c2'); 
        Case[] carr1 = new Case[]{ c1, c2 };
        Case[] carr2 = new Case[]{ c1, c2 };
        DoBeforeUpdateBatch(carr1,carr2);
    }
    
    public static testMethod void testDoBeforeUpdateBatch_SingleRecord() {
        Case c1 = new Case(Subject='E2 TEST CASE c1'); 
        Case[] carr1 = new Case[]{ c1 };
        Case[] carr2 = new Case[]{ c1 };
        DoBeforeUpdateBatch(carr1,carr2);
    }
    
    public static void DoBeforeUpdateBatch(Case[] newCases, Case[] oldCases) {
        l4c = new CaseTriggerHelpers.Log4Case();
        system.assert(oldCases.size()==newCases.size());
        integer i=0;
        integer cnt=newCases.size();
        l4c.add('DoBeforeUpdateBatch:  ' + oldCases.size() + ' cases');
        Case newC;
        Case oldC;
        for(i=0;i<cnt;i++){
            string nowStr = string.ValueOf(system.now());
            l4c.add('Updating case').incIndent();
            try {
                newC = newCases[i];
                oldC = oldCases[i];
                l4c.add('Case Id:  ' + newC.Id).incIndent();
                boolean modifiedCase = DoBeforeUpdate(newC,oldC);
                l4c.decIndent().add('modifiedCase:  ' + modifiedCase);
                if (modifiedCase) {
                    // Update custom mod time
                    l4c.add('Updating last recalc time to:  ' + (nowStr + ' (OK)'));
                    newC.Last_Recalc__c = nowStr + ' (OK)';
                }
            } catch (CaseTriggerHelpers.E2CalcsBadInputException e2ex) {
                system.debug( 'E2 Exception caught for modified Case with id "' + oldC.Id + '":  ' + e2ex.getErrVal() );
                newC.Last_Recalc__c = nowStr + e2ex.getErrVal();
            } catch (Exception e) { 
                l4c.add( 'Unhandled exception on update '+i+' of '+cnt+':  ' + e.getMessage() );
            } finally {
                l4c.decIndent().add('Done with case update').writeOut(newC).clear();
            }
        }
    }

    //
    // The 'before insert' activities performed for each Case record
    //
    public static void DoBeforeInsert(Case c) { 
        try {             
        
            // W2C doesn't default these correctly, so we will
            if (c.Priority == null) { c.Priority = CaseStdFieldControl.GetDefaultPicklistValueFromCase().get(CaseStdFieldControl.PICKLIST_PRIORITY); }
            if (c.Status == null) { c.Status = CaseStdFieldControl.GetDefaultPicklistValueFromCase().get(CaseStdFieldControl.PICKLIST_STATUS); }
                        
            l4c.saveIndent();        
            l4c.add( 'DoBeforeInsert will run on these inputs:' );
            l4c.add( '  c.Priority = ' + c.Priority );
            l4c.add( '  c.Status = ' + c.Status );
            l4c.add( '  c.Entitlement = ' + c.Entitlement__c );
            l4c.incIndent();
            
            if ( c.Entitlement__c != null ) {
            
                // Determine the business hours and SP / Entitlement sobjects
                //-------------------------------
                EntUtil.SPEWrapper spe = new EntUtil.SPEWrapper( c.Entitlement__c );
                l4c.incIndent().add( spe.getMappingsDump() );
                l4c.add(' ==> \''+c.Priority+'\' maps to {'+spe.getEntitledTime( EntUtil.TimeType.EFR, c.Priority ).stripTrailingZeros().toPlainString()+','+spe.getEntitledTime( EntUtil.TimeType.EFW, c.Priority ).stripTrailingZeros().toPlainString()+','+spe.getEntitledTime( EntUtil.TimeType.ERT, c.Priority ).stripTrailingZeros().toPlainString()+'}');
                
                SaveEntitledBusinessHours(c,spe);
                Id bhid = CaseStdFieldControl.getBusinessHours(c);
                l4c.add( 'Business hours copied to Case:  ' + bhid );
                l4c.add( 'spe.getBHId=' + spe.getBHId() );
    
                // Calculate the entitlement
                //-------------------------------
                RecalcEntitlementOnCaseUpdate( c, system.now(), bhid, spe );
            
//            } else {
//            
//                //bid = getCalcsBH(c);
//                //l4c.add( 'Forcing clock start to capture any available minutes' );
//                //StartClockOnCaseUpdate(c,bid);
//           
//            }
            
                //Start/Stop clock
                //-------------------------------
                l4c.add( 'Considering whether to begin w/ clock in stopped or running state' ).incIndent();
                if (StopStatuses.isStop(c.Status)){
                    l4c.add( '...Clock will initially be STOPPED' );
                    StopClockOnCaseUpdate(c,bhid);
                } else {
                    l4c.add( '...Clock will initially be RUNNING' );
                }
                
                //Handle case packs
                //-------------------------------
                l4c.decIndent().add( 'Decrementing case pack if applicable' );
                DecrementCorrespondingCasePack(c,spe);      
                
                l4c.decIndent();
            
            } else {
            	throw new CaseTriggerHelpers.E2CalcsBadInputException( CaseTriggerHelpers.ENT_WAS_NULL );
            }
       
        } catch(Exception ex) {
        
            l4c.add( '>>> EXCEPTION:  ' + ex.getMessage() );
            throw ex;
            
        } finally {
           l4c.restoreIndent();
        }
    }
    
    //
    // before update activities performed for each Case record if relevant fields changed
    //
    public static boolean DoBeforeUpdate(Case newC, Case oldC) {
    
        try {
        
            l4c.saveIndent();

            // Note the changes and values
            String newStatus = newC.Status;
            String oldStatus = oldC.Status;
        
            l4c.incIndent();
            Boolean isEntitlementChanged = newC.Entitlement__c!=oldC.Entitlement__c;
            //Boolean isBHChanged = newC.BusinessHoursId!=oldC.BusinessHoursId;
            Boolean isSubjectChanged = newC.Subject!=oldC.Subject;
            Boolean isPriorityChanged = (newC.Priority==null?null:newC.Priority.toLowerCase()) != 
                (oldC.Priority==null?null:oldC.Priority.toLowerCase());
            boolean bSomeCalculationsInputChanged = (isPriorityChanged || isEntitlementChanged || isSubjectChanged);
                //|| isBHChanged 

            boolean isClockModeChanging = getStartStopModeChanged(newStatus, oldStatus);
            Boolean isClockModeEnteringStopped = isClockModeChanging && StopStatuses.isStop(newStatus);
            boolean isClockModeEnteringRunning = isClockModeChanging && (!StopStatuses.isStop(newStatus));
            boolean isClockModeLeavingStopped = (isClockModeChanging) && (!StopStatuses.isStop(newStatus));
            boolean isClockModeLeavingRunning = (isClockModeChanging) && (StopStatuses.isStop(newStatus));
            
            l4c.add( 'DoBeforeUpdate rcvd inputs:' ).incIndent();
            l4c.add( 'newC.Priority = "' + newC.Priority + '", oldC.Priority="' + oldC.Priority +'"' );
            l4c.add( 'newStatus = "' + newStatus + '", oldStatus="' + oldStatus +'"' );
            l4c.add( 'newC.Entitlement__c = "' + newC.Entitlement__c + '", oldC.Entitlement__c="' + oldC.Entitlement__c +'"' );
            l4c.add( 'Clock state:  ' + ((!isClockModeChanging)?('unchanged in'):((isClockModeLeavingStopped)?('CHANGING from STOPPED =>'):('CHANGING from RUNNING =>'))) + ' ' + ((StopStatuses.isStop(newStatus))?('STOPPED'):('RUNNING')) +' state' );
            l4c.decIndent();
            
            if ((!bSomeCalculationsInputChanged) && (!isClockModeChanging)) {
                l4c.add( 'Unchanged case').decIndent();
                return false;
            }
            
            if (newC.Entitlement__c==null) {
                newC.Entitled_First_Response__c = newC.Entitled_Workaround_Time__c = newC.Entitled_Resolve_Time__c = null;
                newC.Remaining_First_Response__c = newC.Remaining_First_Workaround__c = newC.Remaining_Resolve_Time__c = null;
                //return true;
                throw new CaseTriggerHelpers.E2CalcsBadInputException( CaseTriggerHelpers.ENT_WAS_NULL );
            }
            
            // Try to read Entitlement; don't bother with recalc otherwise.
            //-----------------------------------
//            l4c.add('newC.Entitlement__c='+((newC==null)||(newC.Entitlement__c==null)?('null'):( newC.Entitlement__c )) );
            EntUtil.SPEWrapper spe = new EntUtil.SPEWrapper(newC.Entitlement__c);
            boolean validSPE = spe.hasMapping(newC.Priority);
            
            // If the Entitlement changes, update the Case business hours
            if (isEntitlementChanged) {
                SaveEntitledBusinessHours(newC,spe);
            }
            
            Id bId = CaseStdFieldControl.getBusinessHours(newC); //getCalcsBH(newC);
            l4c.add('bId = '+bId);
            
            // Check for the Priority changes and recalc if so
            //-----------------------------------
            l4c.incIndent().add('validSPE = ' + validSPE);
            
            if (validSPE) {
            
                l4c.incIndent().add( spe.getMappingsDump() );
                l4c.add(' ==> \''+newC.Priority+'\' maps to {'+spe.getEntitledTime( EntUtil.TimeType.EFR, newC.Priority ).stripTrailingZeros().toPlainString()+','+spe.getEntitledTime( EntUtil.TimeType.EFW, newC.Priority ).stripTrailingZeros().toPlainString()+','+spe.getEntitledTime( EntUtil.TimeType.ERT, newC.Priority ).stripTrailingZeros().toPlainString()+'}');
             
                if (bSomeCalculationsInputChanged) // || isClockModeChanging)
                {
                    l4c.add( '--RECALCULATION--').incIndent();
                    if (StopStatuses.isStop(oldStatus)) { 
                        l4c.add('Unstoppng clock before recalc');
                        StartClockOnCaseUpdate(newC,bid); 
                    }
                    Datetime startDate = (newC.CreatedDate!=null) ? newC.CreatedDate : System.now();
                    //if (bSomeCalculationsInputChanged) 
                        RecalcEntitlementOnCaseUpdate(newC, startDate, bId, spe);
                    l4c.decIndent();
                } else {
                    l4c.add('--SKIPPING RECALCULATION-- > ' + 'Reason:  Calculation inputs unchanged' );
                }
                
            } else {
                l4c.add('--SKIPPING RECALCULATION-- > ' + 'Reason:  SP was invalid (does not map "'+ newC.Priority+'") ' );
                l4c.incIndent().add(spe.getMappingsDump() ).decIndent();
            }
            
                            
            // Start or stop clock per Status
            //---------------------------------
            l4c.add( '--SETTING CLOCK STATE--').incIndent();
            if (StopStatuses.isStop(newStatus)) {
                StopClockOnCaseUpdate(newC,bid); 
                l4c.add( 'clock is now stopped');        
            } else {
                StartClockOnCaseUpdate(newC,bid);
                l4c.add( 'clock is now running');
            }
            l4c.decIndent();
            
        } catch (Exception ex) { 
            l4c.add( 'EXCEPTION:  on update w/ msg=' + ex.getMessage() );
            throw ex;
        } finally {
            l4c.restoreIndent();
        }
        
        return true;
    }
    
    static Id[] makeDummyEntsForCaseTests() {
        Stop_Status__c ss = new Stop_Status__c(Name='STOPPED');
        insert ss;     
        UT_SObjectFactory.InterconnectedSObjectsForMASP o = new UT_SObjectFactory.InterconnectedSObjectsForMASP(2);
        o.sp.P1__c='P1';
        o.sp.P2__c='P2';
        update o.sp;
        return new Id[]{ o.eArr[0].Id, o.eArr[0].Id, o.eArr[1].Id, o.eArr[1].Id };        
    }
/*    
    public static testMethod void testDoBeforeUpdatePriorChanged() { 
        String[] pArr = new String[]{'P1','P2'};
        String[] sArr = new String[]{'RUNNING','STOPPED'};
        Id[] eArr = Case_Trigger.makeDummyEntsForCaseTests();
        Case c1 = new Case(Status='RUNNING', Priority=pArr[0], Entitlement__c=eArr[0], Subject='' );
        Case c2 = new Case(Status='RUNNING', Priority=pArr[1], Entitlement__c=eArr[0], Subject='' );
        boolean b = DoBeforeUpdate(c1,c2);
        system.debug( '>>> testDoBeforeUpdatePriorChanged()\n' + l4c.getAllLines() );
        system.assert(b==true);
    }

    public static testMethod void testDoBeforeUpdateCommitDB() { 
        String[] pArr = new String[]{'P1','P2'};
        String[] sArr = new String[]{'RUNNING','STOPPED'};
        Id[] eArr = Case_Trigger.makeDummyEntsForCaseTests();
        Case c1 = new Case(Status='RUNNING', Priority=pArr[0], Entitlement__c=eArr[0], Subject='' );
        Case c2 = new Case(Status='RUNNING', Priority=pArr[1], Entitlement__c=eArr[0], Subject='' );
        boolean b = DoBeforeUpdate(c1,c2);
        system.debug( '>>> testDoBeforeUpdatePriorChanged()\n' + l4c.getAllLines() );
        system.assert(b==true);
    }
        
    public static testMethod void testDoBeforeUpdate() { 
        
        String[] pArr = new String[]{'P1','P2'};
        String[] sArr = new String[]{'RUNNING','STOPPED'};
        Id[] eArr = Case_Trigger.makeDummyEntsForCaseTests();

        for (integer l=0; l<=1; l++) {
            for (integer k=0; k<=1; k++) {
                for (integer j=0; j<=1; j++) {
                    for (integer i=0; i<=1; i++) {
                        try {
                            Case c1 = new Case(Status=sArr[i], Priority=pArr[j], Entitlement__c=eArr[i], Subject=pArr[j] );
                            Case c2 = new Case(Status=sArr[k], Priority=pArr[l], Entitlement__c=eArr[i], Subject=pArr[k] );
                            //Case c3 = new Case(Status=sArr[i], Priority=pArr[l], Entitlement__c=eArr[i], Subject=pArr[k] );
                            //Case c4 = new Case(Status=sArr[i], Priority=pArr[l], Entitlement__c=eArr[i], Subject=pArr[k] , Actual_First_Workaround__c = system.now() );
                            DoBeforeUpdate(c1,c2);
                            //DoBeforeUpdate(c2,c3);
                            //DoBeforeUpdate(c2,c4);
                        } catch(Exception e) { }
                    }
                }
            }
        }
    }

    public static testMethod void testDoBeforeUpdateMore() { 
        
        String[] pArr = new String[]{'P1','P2'};
        String[] sArr = new String[]{'RUNNING','STOPPED'};

        Id[] eArr = Case_Trigger.makeDummyEntsForCaseTests();

        for (integer l=0; l<=1; l++) {
            for (integer k=0; k<=1; k++) {
                for (integer j=0; j<=1; j++) {
                    for (integer i=0; i<=1; i++) {
                        try {
                            Case c2 = new Case(Status=sArr[k], Priority=pArr[l], Entitlement__c=eArr[i], Subject=pArr[k] );
                            Case c3 = new Case(Status=sArr[i], Priority=pArr[l], Entitlement__c=eArr[i], Subject=pArr[k] );
                            Case c4 = new Case(Status=sArr[i], Priority=pArr[l], Entitlement__c=eArr[i], Subject=pArr[k] , Actual_First_Workaround__c = system.now() );
                            DoBeforeUpdate(c2,c3);
                            DoBeforeUpdate(c2,c4);
                        } catch(Exception e) { }
                    }
                }
            }
        }
    }
*/
    public static void StopClockOnCaseUpdate(Case c,Id bid){
        
        try {
            //Id entitlementId = c.Entitlement__c;
            l4c.saveIndent().add('STOP> Stopping clock').incIndent();
            
            DateTime localDate = System.now();
            DateTime entitled_first_response = c.Entitled_First_Response__c;
            DateTime entitled_first_workaround = c.Entitled_Workaround_Time__c;
            DateTime entitled_resolve_time = c.Entitled_Resolve_Time__c;
            Long responseTime = null;
            Long workaroundTime = null;
            Long resolveTime = null;
            Boolean goUpdate = False;
    
            l4c.add('STOP> bid = '+ bid);

            if ((entitled_first_response!=null) && (c.Actual_First_Response__c==null)) {
                    l4c.add('STOP> entitled_first_response='+entitled_first_response.format());
                    l4c.add('STOP> localDate='+localDate.format());
                    responseTime = BHWithFallback.BHDiff(bId, localDate, entitled_first_response);
                    l4c.add('STOP> responseTime='+responseTime+' ms');
                    responseTime = responseTime/1000/60; // result comes in millisecond...convert to min
                    l4c.add('STOP> responseTime='+responseTime+' min');
                    c.Entitled_First_Response__c = null;
                    goUpdate = True; 
            }
            
            if ((entitled_first_workaround!=null) && (c.Actual_First_Workaround__c==null)) {
                    l4c.add('STOP> entitled_first_workaround='+entitled_first_workaround.format());
                    l4c.add('STOP> localDate='+localDate.format());
                    workaroundTime = BHWithFallback.BHDiff(bId, localDate, entitled_first_workaround);
                    l4c.add('STOP> workaroundTime='+workaroundTime+' ms');
                    workaroundTime = workaroundTime/1000/60; // result comes in millisecond...convert to min
                    l4c.add('STOP> workaroundTime='+workaroundTime+' min');
                    c.Entitled_Workaround_Time__c = null;
                    goUpdate = True;
            }
            
            if (entitled_resolve_time!=null) {
                    resolveTime = BHWithFallback.BHDiff(bId, localDate, entitled_resolve_time);
                    resolveTime = resolveTime/1000/60; // result comes in millisecond...convert to min
                    c.Entitled_Resolve_Time__c = null;
                    goUpdate = True;
            }
            
            if(goUpdate) {
                c.Remaining_First_Response__c = responseTime; //(null==c.Remaining_First_Response__c) ? responseTime : (responseTime + c.Remaining_First_Response__c);
                c.Remaining_First_Workaround__c = workaroundTime; //(null==c.Remaining_First_Workaround__c) ? workaroundTime : (workaroundTime + c.Remaining_First_Workaround__c);
                c.Remaining_Resolve_Time__c = resolveTime; //(null==c.Remaining_Resolve_Time__c) ? resolveTime  : (resolveTime + c.Remaining_Resolve_Time__c);
                l4c.add('STOP> Updated Rs:  ' + c.Remaining_First_Response__c + ', ' + c.Remaining_First_Workaround__c + ',' + c.Remaining_Resolve_Time__c );
            } else {
                l4c.add('STOP> FAILED:  Entitled times are in the past:').incIndent().add('c=' + c).decIndent();
            }
        
        } catch(Exception e) { 
           l4c.addError('STOP> EXCEPTION DURING STOP CLOCK:  ' + e.getMessage());
        } finally {
           l4c.restoreIndent().add('Done working on clock');
        }
    }

    //
    // Start Clock Task
    //
    public static void StartClockOnCaseUpdate(Case c,Id bid){
    
        try {
            
            string msg = 'START> Started clock:  ';
            l4c.saveIndent().incIndent();
            l4c.add('START> bid = '+ bid);
            
            DateTime localDate = System.now();
            Double remaining_first_response = c.Remaining_First_Response__c;
            Double remaining_first_workaround = c.Remaining_First_Workaround__c;
            Double remaining_resolve_time = c.Remaining_Resolve_Time__c;
    
            DateTime responseTime, workaroundTime, resolveTime;
            
            //
            //The remaining time are saved in minutes, so convert them to hour before sending
            //
            if(remaining_first_response!=null){
                l4c.add('START> c.Entitled_First_Response__c = ' + c.Entitled_First_Response__c);
                Datetime startFrom = (c.Entitled_First_Response__c!=null) ? c.Entitled_First_Response__c : localDate;
                l4c.add('START> startFrom = ' + startFrom.format());
                responseTime = BHWithFallback.BHAdd(bId, startFrom, (remaining_first_response*60*1000L).longValue());
                l4c.add('START> responseTime = ' + responseTime.format());
                c.Entitled_First_Response__c = responseTime;
                msg += c.Entitled_First_Response__c.format() + ', ';
            }
            if(remaining_first_workaround!=null){
                l4c.add('START> c.Entitled_Workaround_Time__c = ' + c.Entitled_Workaround_Time__c);
                Datetime startFrom = (c.Entitled_Workaround_Time__c!=null) ? c.Entitled_Workaround_Time__c : localDate;
                l4c.add('START> startFrom = ' + startFrom.format());
                workaroundTime = BHWithFallback.BHAdd(bId, startFrom, (remaining_first_workaround*60*1000L).longValue());
                l4c.add('START> workaroundTime = ' + workaroundTime.format());
                c.Entitled_Workaround_Time__c = workaroundTime;
                msg += c.Entitled_Workaround_Time__c.format() + ', ';
            }
            if(remaining_resolve_time!=null){
                Datetime startFrom = (c.Entitled_Resolve_Time__c!=null) ? c.Entitled_Resolve_Time__c : localDate;
                resolveTime = BHWithFallback.BHAdd(bId, startFrom, (remaining_resolve_time*60*1000L).longValue());
                c.Entitled_Resolve_Time__c = resolveTime;
                msg += c.Entitled_Resolve_Time__c.format();
            }
    
            c.Remaining_First_Response__c = null;
            c.Remaining_First_Workaround__c = null;
            c.Remaining_Resolve_Time__c = null;
            
            l4c.add('START> Updated Es:  ' + msg );
        
        } catch(Exception e) { 
           l4c.addError('START> EXCEPTION DURING STOP CLOCK:  ' + e.getMessage());
        } finally {
           l4c.restoreIndent().add('START> Done working on clock');
        }
    }
    
    //
    // Recalculate Entitlement when the priority is changed
    //
    // Preconditions:  - c.BusinessHoursId must be a valid business hours id that is active in this org.
    //                 - c.Entitlement__c.Support_Program__c must be traverseable.  That
    //                     SP need not be committed to the database, but it must map 
    //                     c.Priority or have a wildcard mapping ('*' in some name).
    public static void RecalcEntitlementOnCaseUpdate(Case c, DateTime startDate, Id bId, EntUtil.SPEWrapper spe){
        
        system.assertNotEquals(startDate,null);

        l4c.incIndent();
        
        Decimal efr = spe.getEntitledTime( EntUtil.TimeType.EFR, c.Priority );
        Decimal efw = spe.getEntitledTime( EntUtil.TimeType.EFW, c.Priority );
        Decimal ert = spe.getEntitledTime( EntUtil.TimeType.ERT, c.Priority );
        
        //if(c.Actual_First_Response__c==null){
            
            DateTime responseTime = BHWithFallback.BHAdd(bId, startDate, (efr*60*60*1000L).longValue());
            c.Entitled_First_Response__c = responseTime;
        ///}
        
        //if(c.Actual_First_Workaround__c==null){
            DateTime workaroundTime = BHWithFallback.BHAdd(bId, startDate, (efw*60*60*1000L).longValue());
            c.Entitled_Workaround_Time__c = workaroundTime;
        //}
        
        DateTime resolveTime = BHWithFallback.BHAdd(bId, startDate, (ert*60*60*1000L).longValue());        
        c.Entitled_Resolve_Time__c = resolveTime;
        
        c.Remaining_First_Response__c = null;
        c.Remaining_First_Workaround__c = null;
        c.Remaining_Resolve_Time__c = null;
        
        l4c.add( 'efr:  ' + startDate.format() + '+' + efr + ' b. hours = ' + c.Entitled_First_Response__c );
        l4c.add( 'efw: ' + startDate.format() + '+' + efw + ' b. hours = ' + c.Entitled_Workaround_Time__c );
        l4c.add( 'ert: ' + startDate.format() + '+' + ert + ' b. hours = ' + c.Entitled_Resolve_Time__c );
        l4c.add( 
            //'results:  entitled times=('+c.Entitled_First_Response__c+','+c.Entitled_Workaround_Time__c+','+
            //c.Entitled_Resolve_Time__c+'), '+ ' + 
            'remaining minutes=('+c.Remaining_First_Response__c + ',' 
            + c.Remaining_First_Workaround__c+','
            +  c.Remaining_Resolve_Time__c + ')');
          
        l4c.decIndent();
    }
  
  
    //Returns true if status has changed from start to stop or stop to start
    public static boolean getStartStopModeChanged(String newStatus, String oldStatus){
        return ( (StopStatuses.isStop(newStatus)) != (StopStatuses.isStop(oldStatus)) );
    }
/*    
    public static Id getCalcsBH(Case c) {
        if (c.Entitlement__c != null) {
            return EntUtil.getEntitledBHId(c.Entitlement__c);
        }  
        else return CaseStdFieldControl.getBusinessHours(c);
    }
*/     
    public static void DecrementCorrespondingCasePack(Case c,EntUtil.SPEWrapper spe) {
//        if(c.Entitlement__c==null) {
//            throw new CaseTriggerHelpers.E2CalcsBadInputException( CaseTriggerHelpers.ENT_WAS_NULL );
//        }
        if (spe.UsesCasePack())
            EntUtil.DecrementCasePack(c.Entitlement__c);
    }

    public static void SaveEntitledBusinessHours(Case c,EntUtil.SPEWrapper spe){
        Id bid;
        //Boolean bBHSaveOK = false;
        try {
            //bid = EntUtil.getEntitledBHId(c.Entitlement__c);
            bid = spe.getBHId();
            //bBHSaveOK = 
                CaseStdFieldControl.setBusinessHours(c,bid);
            l4c.add('Saved Case.BusinessHoursId = ' + bId);
        } catch(Exception e) {
            //bBHSaveOK = false;
            l4c.add('Failed to save Case.BusinessHoursId = ' + bId);
        }
        //if (!bBHSaveOK) l4c.add('Failed to save Case.BusinessHoursId = ' + bId);
        //else 
        
    }
 
/*    public static testMethod void testSaveEntitledBusinessHours()
    {
       try {
            Entitlement__c entNoBH = new Entitlement__c(Name='Test Entitlement w/o BH', Business_Hours__c=null);
            insert entNoBH; 
            
            Case cEntNoBH = new Case(Entitlement__c = entNoBH.Id);
            SaveEntitledBusinessHours(cEntNoBH);
       } catch (CaseTriggerHelpers.E2CalcsBadInputException e2ex) {
            System.debug('Caught excepted exception -- OK');
       } catch (Exception e) {
            System.debug('Caught unexcepted exception -- check org setup'+e.getMessage());
       }
    }    */
    
}